
/**		
 * 
 *	uk.co.lmfm.guppy.model.SettingsProxy
 *	
 *	@version 1.00 | Jan 12, 2010
 *	@author Justin Windle
 *  
 **/
 
package uk.co.lmfm.guppy.model 
{
	import uk.co.lmfm.data.SQLManager;
	import uk.co.lmfm.guppy.application.enum.ApplicationConstants;
	import uk.co.lmfm.guppy.model.vo.AlertVO;
	import uk.co.lmfm.guppy.model.vo.AppStateVO;
	import uk.co.lmfm.guppy.model.vo.SettingsVO;
	import uk.co.lmfm.guppy.model.vo.ValidatedSettingsVO;
	import uk.co.lmfm.util.FileUtil;

	import org.osflash.thunderbolt.Logger;
	import org.puremvc.as3.multicore.interfaces.IProxy;
	import org.puremvc.as3.multicore.patterns.proxy.Proxy;

	import flash.desktop.NativeProcess;
	import flash.desktop.NativeProcessStartupInfo;
	import flash.events.FileListEvent;
	import flash.events.NativeProcessExitEvent;
	import flash.events.ProgressEvent;
	import flash.events.SQLErrorEvent;
	import flash.filesystem.File;

	/**
	 * SettingsProxy
	 */
	public class SettingsProxy extends Proxy implements IProxy 
	{
		//	----------------------------------------------------------------
		//	CONSTANTS
		//	----------------------------------------------------------------

		public static const NAME : String = "SettingsProxy";

		private static const FIND_PATH : String = "/usr/bin/find";

		private static const DATABASE_NAME : String = "guppy.db";
		private static const SETTINGS_TABLE_NAME : String = "settings";		private static const CONFIG_TABLE_NAME : String = "config";		private static const CONFIG_FILE : String = "config.xml";

		//	----------------------------------------------------------------
		//	PRIVATE MEMBERS
		//	----------------------------------------------------------------

		private var _sqlManager : SQLManager;
		private var _hasPreferences : Boolean;
		private var _autoSettings : SettingsVO;
		private var _bestWorkspaceMatch : String;
		private var _minWorkspaceIndex : int;
		private var _database : File;
		private var _searches : int;

		private var _appFind : NativeProcess = new NativeProcess();		private var _workspaceFind : NativeProcess = new NativeProcess();

		//	----------------------------------------------------------------
		//	CONSTRUCTOR
		//	----------------------------------------------------------------

		public function SettingsProxy(data : Object = null)
		{
			super(NAME, data);
		}

		//	----------------------------------------------------------------
		//	PUBLIC METHODS
		//	----------------------------------------------------------------

		override public function onRegister() : void
		{
			super.onRegister();
			
			_database = File.applicationStorageDirectory.resolvePath(DATABASE_NAME);
			
			var databaseExists : Boolean = _database.exists;

			_sqlManager = new SQLManager(_database);
			_sqlManager.addEventListener(SQLErrorEvent.ERROR, onSQLError);
			
			if(!databaseExists)
			{
				var defaultSettings : SettingsVO = new SettingsVO();
				
				defaultSettings.workspaceLocation = "";
				defaultSettings.libraryPath = "";
				
				writeSettings(defaultSettings);
				
				var defaultAppState : AppStateVO = new AppStateVO();
				defaultAppState.lastRevision = 0;
				defaultAppState.sdks = "";
								writeAppState(defaultAppState);
			}
			
			_hasPreferences = databaseExists;
		}

		public function autoSetup() : void
		{
			_autoSettings = new SettingsVO();
			_autoSettings.username = File.userDirectory.name.substr(0, 1).toUpperCase() + File.userDirectory.name.substr(1);
			
			var libDir : File = File.userDirectory.resolvePath("Guppy");			if(libDir.exists) libDir = File.documentsDirectory.resolvePath("Guppy");
			if(!libDir.exists) _autoSettings.libraryPath = libDir.nativePath;
			
			if(NativeProcess.isSupported)
			{
				var appStart : NativeProcessStartupInfo = new NativeProcessStartupInfo();
				
				appStart.executable = new File(FIND_PATH);
				appStart.arguments = new Vector.<String>();
				
				appStart.arguments.push("find");
				appStart.arguments.push("/applications");
				appStart.arguments.push("-name");
				appStart.arguments.push("*.app");				appStart.arguments.push("-maxdepth");				appStart.arguments.push("4");

				_appFind.addEventListener(NativeProcessExitEvent.EXIT, onAppSearchComplete);
				_appFind.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onAppSearchProgress);
				_appFind.start(appStart);
				
				var workspace : File = new File().resolvePath("/workspace");
				//if(!workspace.exists) workspace = File.userDirectory.resolvePath("workspace");				//if(!workspace.exists) workspace = File.documentsDirectory.resolvePath("workspace");

				if(workspace.exists)
				{
					_autoSettings.workspaceLocation = workspace.nativePath;
				}
				else
				{
					_bestWorkspaceMatch = "";
					_minWorkspaceIndex = 999999;
					
					var workspaceStart : NativeProcessStartupInfo = new NativeProcessStartupInfo();
				
					workspaceStart.executable = new File(FIND_PATH);
					workspaceStart.arguments = new Vector.<String>();
					
					// find -E /users/justin -name .metadata -not -regex '.*/[.]Trash/*.*'

					workspaceStart.arguments.push("find");					//workspaceStart.arguments.push("-E"); // Doesn't seem to like this argument					workspaceStart.arguments.push(File.userDirectory.nativePath);					workspaceStart.arguments.push("-name");					workspaceStart.arguments.push(".metadata");					workspaceStart.arguments.push("-not");					workspaceStart.arguments.push("-regex");					workspaceStart.arguments.push("'.*/[.]Trash/*.*'");					workspaceStart.arguments.push("-maxdepth");					workspaceStart.arguments.push("4");

					_workspaceFind.addEventListener(NativeProcessExitEvent.EXIT, onWorkspaceSearchComplete);
					_workspaceFind.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onWorkspaceSearchProgress);
					_workspaceFind.start(workspaceStart);
				
					trace("Searing for workspaces...");
				}
			}
			
			checkAutoSetupProgress();
		}

		public function cancelAutoSetup() : void
		{
			trace("Autosetup Cancelled");
			
			_workspaceFind.removeEventListener(NativeProcessExitEvent.EXIT, onWorkspaceSearchComplete);
			_workspaceFind.removeEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onWorkspaceSearchProgress);
			_workspaceFind.exit();
			
			_appFind.removeEventListener(NativeProcessExitEvent.EXIT, onAppSearchComplete);
			_appFind.removeEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onAppSearchProgress);
			_appFind.exit();
			
			sendNotification(ApplicationConstants.ENABLE_MENU);
		}

		public function readConfig() : XML
		{
			var config : File = new File().resolvePath(readSettings().libraryPath).resolvePath(CONFIG_FILE);
			
			if(config.exists)
			{
				return FileUtil.readXML(config);
			}
			else
			{
				Logger.error("Error reading config file from library. Please check that config.xml is in the root of your Guppy library");
				return new XML();
			}
		}

		public function readAppState() : AppStateVO
		{
			return _sqlManager.readObject(AppStateVO, CONFIG_TABLE_NAME) as AppStateVO;
		}

		public function readSettings() : SettingsVO
		{
			return _sqlManager.readObject(SettingsVO, SETTINGS_TABLE_NAME) as SettingsVO;
		}

		public function writeAppState(state : AppStateVO) : void
		{
			_sqlManager.writeObject(state, CONFIG_TABLE_NAME);
		}

		public function writeSettings(settings : SettingsVO) : void
		{
			_sqlManager.writeObject(settings, SETTINGS_TABLE_NAME);
		}

		public function validateSettings(settings : SettingsVO) : ValidatedSettingsVO
		{
			var result : ValidatedSettingsVO = new ValidatedSettingsVO();
			
			result.subject = settings;
			
			if(!isValidDirectory(settings.workspaceLocation))
			{
				result.results.workspaceLocation = 'false';
				result.valid = false;
			}
			
			if(!isValidDirectory(settings.libraryPath))
			{
				result.results.libraryPath = 'false';
				result.valid = false;
			}
			
			if(!isValidDirectory(settings.eclipsePath))
			{
				result.results.eclipsePath = 'false';
				result.valid = false;
			}
			
			return result;
		}

		public function isValidDirectory(path : String) : Boolean
		{
			var dir : File = new File();
			
			try
			{
				dir.nativePath = path;
			}
			catch(error : ArgumentError)
			{
				return false;
			}
			
			return dir.exists && dir.isDirectory;
		}

		//	----------------------------------------------------------------
		//	PRIVATE METHODS
		//	----------------------------------------------------------------

		private function checkAutoSetupProgress() : void
		{
			sendNotification(ApplicationConstants.AUTO_SETUP_PROGRESS, _autoSettings);
			
			if(!_workspaceFind.running && !_appFind.running)
			{
				sendNotification(ApplicationConstants.AUTO_SETUP_COMPLETE, _autoSettings);
				sendNotification(ApplicationConstants.ENABLE_MENU);
				
				var alert : AlertVO = new AlertVO();
				alert.title = "Auto Setup Complete!";
				alert.message = "Please review the settings before saving...";
				
				sendNotification(ApplicationConstants.SHOW_ALERT, alert);
			}
		}

		private function traverseDirectories(directory : File) : void
		{
			//var depth : int = directory.nativePath.split("/").length;
			//trace("Current depth: " + depth);

			_searches++;
			
			directory.addEventListener(FileListEvent.DIRECTORY_LISTING, onDirectoryListing);
			directory.getDirectoryListingAsync();
		}

		private function onDirectoryListing(event : FileListEvent) : void
		{
			var directory : File = event.currentTarget as File;
			var directories : Array = event.files;
			directory.removeEventListener(FileListEvent.DIRECTORY_LISTING, onDirectoryListing);
			
			for each (var file : File in directories) 
			{
				if(file.isDirectory)
				{
					/*if(file.nativePath.indexOf(METADATA_PATH) > 0)
					{
					var workspace : WorkspaceVO = new WorkspaceVO();
						
					workspace.location = file.parent;
					workspace.name = file.parent.name;
					workspace.isConfigured = false;

					var config : File = workspace.location.resolvePath(METADATA_PATH).resolvePath(GUPPY_PREFS_PATH);
						
					if(config.exists)
					{
					var data : XML = FileUtil.readXML(config);
					if(parseInt(data.revision) == svnRevision) workspace.isConfigured = true;
					}

					_workspaces.push(workspace);
					sendNotification(ApplicationConstants.FOUND_WORKSPACE, workspace);
					}
					else
					{
					traverseDirectories(file);
					}*/
					//trace("Dir:\t" + file.name);
					traverseDirectories(file);
				}
				else
				{
					//trace("File:\t" + file.name);
					if(file.name.match(/flash/gi)[0])
					{
						trace(file.name);
					}
				}
			}
			
			_searches--;
			
			if(_searches < 1)
			{
				// Complete
				Logger.info("Directory search complete");
			}
		}

		//	----------------------------------------------------------------
		//	EVENT HANDLERS
		//	----------------------------------------------------------------

		private function onAppSearchComplete(event : NativeProcessExitEvent) : void
		{
			_appFind.exit();
			checkAutoSetupProgress();
		}

		private function onAppSearchProgress(event : ProgressEvent) : void
		{
			var apps : String = _appFind.standardOutput.readUTFBytes(_appFind.standardOutput.bytesAvailable);
			var list : Array = apps.split("\n");
			var app : String;
			
			for (var i : int = 0;i < list.length;i++)
			{
				app = list[i];
				
				if(app.search(/debug.+?flash.+player.app$/gi) > -1)
				{
					Logger.info("Possible Player match: " + app);
					_autoSettings.flashPlayer = app;
				}
				else if(app.search(/flash cs([0-9]).app$/gi) > -1)
				{
					Logger.info("Possible IDE match: " + app);
					_autoSettings.flashIDE = app;
				}
				else if(app.search(/(eclipse|fdt)\s?\d?\.?\d?.app/gi) > -1)
				{
					if(app.match(/uninstall/gi).length < 1)
					{
						Logger.info("Possible FDT match: " + app);
						_autoSettings.eclipsePath = new File().resolvePath(app).parent.nativePath;
					}
				}
			}
			
			checkAutoSetupProgress();
		}

		private function onWorkspaceSearchComplete(event : NativeProcessExitEvent) : void
		{
			var workspace : File = new File().resolvePath(_bestWorkspaceMatch);
			
			if(workspace.exists)
			{
				_autoSettings.workspaceLocation = workspace.parent.nativePath;
			}
			
			_workspaceFind.exit();
			checkAutoSetupProgress();
		}

		private function onWorkspaceSearchProgress(event : ProgressEvent) : void
		{
			var workspaces : String = _workspaceFind.standardOutput.readUTFBytes(_workspaceFind.standardOutput.bytesAvailable);
			var list : Array = workspaces.split("\n");
			var path : String;
			var index : int;
			
			for (var i : int = 0;i < list.length;i++)
			{
				path = list[i].toLowerCase();
				if(path.indexOf(".trash") != -1) continue;
				
				index = path.indexOf(".metadata");
				
				if(index > 0 && index < _minWorkspaceIndex)
				{
					_minWorkspaceIndex = index;
					_bestWorkspaceMatch = path;
				}
			}
		}

		private function onSQLError(event : SQLErrorEvent) : void
		{
			Logger.error(event.text);
		}

		//	----------------------------------------------------------------
		//	PUBLIC ACCESSORS
		//	----------------------------------------------------------------

		public function get hasPreferences() : Boolean
		{
			return _hasPreferences;
		}
	}
}
